package org.raftkv.raft.core;

import org.raftkv.raft.core.storage.Storage;
import org.raftkv.raft.exceptions.RaftException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 创建一个 Raft 所需要的配置
 *
 * @author wzy
 */
public class Config {

    /**
     * 节点 id
     */
    private long id;

    /**
     * 选举超时的 tick
     */
    private int electionTick;

    /**
     * 心跳超时的 tick
     */
    private int heartbeatTick;

    /**
     * 存储接口
     */
    private Storage storage;

    /**
     * 最后一个被应用到状态机的 index（重启 raft 时指定）
     */
    private long applied;

    /**
     * 每个消息最大的大小
     */
    private long maxSizePerMsg;

    private static final Logger LOGGER = LoggerFactory.getLogger(Config.class);

    private long maxCommittedSizePerReady;

    private long maxUncommittedEntriesSize;

    /**
     * 记 leader 是否需要检查集群中超过半数节点的活跃性，如果在选举超时内没有满足该条件，则 leader 切换到 follower 状态
     */
    private boolean checkQuorum;

    private int maxInflightMsgs;

    private boolean preVote;

    private int readOnlyOption;

    private boolean disableProposalForwarding;

    public Config() {

    }

    /**
     * 校验配置
     */
    public void validate() {
        if (id == Raft.NONE) {
            throw new RaftException("cannot use none as id");
        }
        if (heartbeatTick <= 0) {
            throw new RaftException("heartbeat tick must be greater than 0");
        }
        if (electionTick <= heartbeatTick) {
            throw new RaftException("election tick must be greater than heartbeat tick");
        }
        if (storage == null) {
            throw new RaftException("storage cannot be null");
        }
        if (maxUncommittedEntriesSize == 0) {
            maxUncommittedEntriesSize = Raft.NO_LIMIT;
        }
        if (maxCommittedSizePerReady == 0) {
            maxCommittedSizePerReady = maxSizePerMsg;
        }
        if (maxInflightMsgs <= 0) {
            throw new RaftException("max inflight messages must be greater than 0");
        }
        if (readOnlyOption == Raft.READ_ONLY_LEASE_BASED && !checkQuorum) {
            throw new RaftException("CheckQuorum must be enabled when ReadOnlyOption is ReadOnlyLeaseBased");
        }
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public int getElectionTick() {
        return electionTick;
    }

    public void setElectionTick(int electionTick) {
        this.electionTick = electionTick;
    }

    public int getHeartbeatTick() {
        return heartbeatTick;
    }

    public void setHeartbeatTick(int heartbeatTick) {
        this.heartbeatTick = heartbeatTick;
    }

    public Storage getStorage() {
        return storage;
    }

    public void setStorage(Storage storage) {
        this.storage = storage;
    }

    public long getApplied() {
        return applied;
    }

    public void setApplied(long applied) {
        this.applied = applied;
    }

    public long getMaxSizePerMsg() {
        return maxSizePerMsg;
    }

    public void setMaxSizePerMsg(long maxSizePerMsg) {
        this.maxSizePerMsg = maxSizePerMsg;
    }

    public boolean isCheckQuorum() {
        return checkQuorum;
    }

    public void setCheckQuorum(boolean checkQuorum) {
        this.checkQuorum = checkQuorum;
    }

    public long getMaxCommittedSizePerReady() {
        return maxCommittedSizePerReady;
    }

    public void setMaxCommittedSizePerReady(long maxCommittedSizePerReady) {
        this.maxCommittedSizePerReady = maxCommittedSizePerReady;
    }

    public long getMaxUncommittedEntriesSize() {
        return maxUncommittedEntriesSize;
    }

    public void setMaxUncommittedEntriesSize(long maxUncommittedEntriesSize) {
        this.maxUncommittedEntriesSize = maxUncommittedEntriesSize;
    }

    public int getMaxInflightMsgs() {
        return maxInflightMsgs;
    }

    public void setMaxInflightMsgs(int maxInflightMsgs) {
        this.maxInflightMsgs = maxInflightMsgs;
    }

    public boolean isPreVote() {
        return preVote;
    }

    public void setPreVote(boolean preVote) {
        this.preVote = preVote;
    }

    public int getReadOnlyOption() {
        return readOnlyOption;
    }

    public void setReadOnlyOption(int readOnlyOption) {
        this.readOnlyOption = readOnlyOption;
    }

    public boolean isDisableProposalForwarding() {
        return disableProposalForwarding;
    }

    public void setDisableProposalForwarding(boolean disableProposalForwarding) {
        this.disableProposalForwarding = disableProposalForwarding;
    }

}
